package com.googlecode.mailnet.simulation;

import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import com.googlecode.mailnet.info.ILinkInfo;
import com.googlecode.mailnet.info.IMessageListener;
import com.googlecode.mailnet.info.INodeInfo;
import com.googlecode.mailnet.info.ISimulationInfo;
import com.googlecode.mailnet.netmodel.Link;
import com.googlecode.mailnet.netmodel.MessageFlow;
import com.googlecode.mailnet.netmodel.NetworkConfiguration;
import com.googlecode.mailnet.netmodel.Node;

/**
 * @author Iskander Akishev
 * TODO handle message creation, resending and delivering and notify listeners
 */
class SimulationState implements ISimulationInfo {

	final NetworkConfiguration networkConfiguration;
//	final SimulationStatistics simulationStatistics;

	final HashMap<Node, NodeState> nodeStates;
	final Map<Link, LinkState> linkStates;
	final Map<MessageFlow, MessageFlowState> flowStates;

	int currentStepNumber;

	SimulationState(NetworkConfiguration networkConfiguration) {
		this.networkConfiguration = networkConfiguration;
//		this.simulationStatistics = simulationStatistics;

		nodeStates = new HashMap<Node, NodeState>();
		for (Node node : networkConfiguration.getNodes()) {
			nodeStates.put(node, new NodeState(this, node));
		}

		linkStates = new HashMap<Link, LinkState>();
		for (Link link : networkConfiguration.getLinks()) {
			linkStates.put(link, new LinkState(this, link));
		}

		flowStates = new HashMap<MessageFlow, MessageFlowState>();
		for (MessageFlow flow : networkConfiguration.getFlows()) {
			flowStates.put(flow, new MessageFlowState(this, flow));
		}
	}

	void makeStep() {
		currentStepNumber++;

        // Clear per-step statistics
        for (LinkState linkState : linkStates.values()) {
            linkState.clearStatistics();
        }
		for (NodeState nodeState : nodeStates.values()) {
			nodeState.clearStatistics();
		}

		// Imitating network elements failures.
		for (NodeState nodeState : nodeStates.values()) {
			nodeState.simulateProbableFailure();
		}
		for (LinkState linkState : linkStates.values()) {
			linkState.simulateProbableFailure();
		}

		// Accepting incoming messages from all links.
		for (LinkState linkState : linkStates.values()) {
			linkState.deliverMessages();
		}

		// Generating new and regenerating undelivered messages.
		for (MessageFlowState flowState : flowStates.values()) {
			flowState.generateMessages();
		}

		// Distributing all received and generated messages over sending-queues.
		for (NodeState nodeState : nodeStates.values()) {
			nodeState.distributeMessagesOverQueues();
		}

		// Pushing outgoing messages into links.
		for (LinkState linkState : linkStates.values()) {
			linkState.consumeMessagesFromQueues();
		}
	}
	
	public Map<Link, ? extends ILinkInfo> getLinkInfoSet() {
		return linkStates;
	}

	public Map<Node, ? extends INodeInfo> getNodeInfoSet() {
		return nodeStates;
	}

	public int now() {
		return currentStepNumber;
	}
	
	final Set<IMessageListener> messageListeners = new LinkedHashSet<IMessageListener>();

	public void addMessageListener(IMessageListener listener) {
		messageListeners.add(listener);
	}

	public void removeMessageListener(IMessageListener listener) {
		messageListeners.remove(listener);
	}
	
}
